#!/usr/bin/python  
# -*- coding: utf-8 -*-
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor
from twisted.internet.error import CannotListenError
from sys import stdout
import sys
import threading
import time
from threading import Timer
from threading import ThreadError
import re

##Définition des variables##
mdp="aventure"
port=8008
crossdomain="<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\"><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\"/></cross-domain-policy>"
if(len(sys.argv)>1):
	try:
		port=int(sys.argv[1])
	except(ValueError):
		pass
	if(len(sys.argv)>2):
		mdp=sys.argv[2]
class Clients:
	def __init__(self,max_clients):
		self.tab_idx=[]
		self.tab_class=[]
		self.park=Zones()
		self.tab_zones=[]#contient les salons ds lesquels se trouvent la personne
		self.set_max_clients(max_clients)
		self.unik=0
	def set_max_clients(self,max_clients):
		self.max=max_clients
	def max_clients(self):
		return self.max
	def goto(self,iid,name):
		if(iid>0 and len(name)>0):
			if(iid in self.tab_idx):
				if(self.park.enter(iid,name)):
					self.tab_zones[self.tab_idx.index(iid)].append(name)
					self.__write__(iid,name,"g"+str(iid)+":"+name)

	def left(self,iid,name):
		if(iid>0 and len(name)>0):
			if(iid in self.tab_idx):
				if(self.park.is_inside(iid,name)):
					self.tab_zones[self.tab_idx.index(iid)].remove(name)
					self.__write__(iid,name,"l"+str(iid)+":"+name)
					self.park.left(iid,name)
				
	def add_client(self,client):
		if(len(self.tab_idx)<self.max):
			self.unik=iid=self.unik+1
			self.tab_idx.append(iid)
			self.tab_class.append(client)
			self.tab_zones.append([])
			return iid
		raise "maxClientsError"
	#update profil en envoyant une seule fois l'info par personne présente dans au moins un salon en commun avec l'updaté!
	def write_all(self,iid,msg):
		if(iid>0):
			if(iid in self.tab_idx):
				index=self.tab_idx.index(iid)
				tab=self.tab_zones[index][:]
				personnes=[]
				for i in tab:
					if(self.park.is_inside(iid,i)):
						for j in self.park.get(i):
							#if(j in self.tab_idx and j!=iid):
								index=self.tab_idx.index(j)
								if(not (index in personnes)):
									personnes.append(index)	
				for i in personnes:
					self.tab_class[i].write(msg)
	def del_client(self,iid):
		if(iid>0):
			if(iid in self.tab_idx):
				index=self.tab_idx.index(iid)
				tab=self.tab_zones[index][:]
				for i in tab:
					self.left(iid,i)
				self.tab_idx.remove(iid)
				self.tab_zones.pop(index)
				self.tab_class.pop(index)
	def __write__(self,iid,name,msg,dest=0):
		if(iid>0 and (not dest==iid)):
			if(iid in self.tab_idx):
				if(self.park.is_inside(iid,name)):
					if(dest==0):#on ecrit a tout le salon
						for i in self.park.get(name):
							#if(i in self.tab_idx and i!=iid):
								index=self.tab_idx.index(i)
								self.tab_class[index].write(msg)
					else:#on ecrit a une personne particuliere
						if(dest in self.tab_idx):
							if(self.park.is_inside(dest,name)):
								index=self.tab_idx.index(dest)
								self.tab_class[index].write(msg)
								
						
	def write(self,iid,name,msg,dest=0):
		self.__write__(iid,name,"s"+str(len(name))+":"+name+str(iid)+":"+msg,dest)
class Zones:
	def __init__(self):
		self.names=[]
		self.zones=[]
	#entrer dans un salon
	def enter(self,iid,name):
		if(name in self.names):
			id_zone=self.names.index(name)
			if(not iid in self.zones[id_zone]):
				self.zones[id_zone].append(iid)
				return True
		else:
			if(len(name)>0):
				id_zone=len(self.names)
				self.names.append(name)
				self.zones.append([iid])
				return True
		return False
	#quitter un salon
	def left(self,iid,name):
		if(name in self.names):
			id_zone=self.names.index(name)
			if(iid in self.zones[id_zone]):
				self.zones[id_zone].remove(iid)
				if(len(self.zones[id_zone])==0):
					self.zones.pop(id_zone)
					self.names.pop(id_zone)
				return True
		return False
	#nombre de personnes dans un salon ou le nombre de salon(None)
	def len(self,name=None):
		if(name==None):
			return len(self.names)
		if(name in self.names):
			id_zone=self.names.index(name)
			return len(self.zones[id_zone])
		else:
			return 0
	#personne[id] dans le salon[name] ?
	def is_inside(self,iid,name):
		if(name in self.names):
			id_zone=self.names.index(name)
			return iid in self.zones[id_zone]
		return False
	#retourne les id des personnes présentes
	def get(self,name):
		if(name in self.names):
			id_zone=self.names.index(name)
			return self.zones[id_zone]
		return []
class QOTD(Protocol):
	def connectionMade(self):
		self.isParked=False
		self.id=0
		try:
			self.id=self.factory.clients.add_client(self)
			self.factory.num_clients=self.factory.num_clients+1
			self.factory.info.num_clients(self.factory.num_clients)
			#on supprime les infos précédentes s'il y'en a
			self.factory.save.rem(self.id)
			try:
				t = Timer(15.0, self.isNotParked)
				t.start() # after 30 seconds, "hello, world" will be printed
			except(ThreadError):
				pass
		except("maxClientsError"):
			#self.factory.info.msg("RangeError****")
			self.transport.write('[Error=maxClients]')
			try:
				t = Timer(3.0, self.close)
				t.start()
			except(ThreadError):
				self.close
	def connectionLost(self, reason):
		if(self.id>0):
			self.factory.save.rem(self.id)
			self.factory.clients.del_client(self.id)
			self.factory.num_clients = self.factory.num_clients-1
			self.factory.info.num_clients(self.factory.num_clients)
	def close(self):
		self.transport.loseConnection()
	def dataReceived(self, data):
		if(self.id>0):
			#nettoyage des lignes
			tab=data.split("][")
			for i in tab:
				while(i[0]=="[" or i[0]=="\n" or i[0]=="\r"):
					i=i[1:]
				while(i[-1]=="]" or i[-1]=="\n" or i[-1]=="\r"):
					i=i[0:len(i)-1]
			#lecture des lignes
				if(len(i)>0):
					print "in "+str(self.id)+" : "+i
					if(i[0]=="i"):
						
						if(i[1:]==mdp):
							self.write("i"+str(self.id))
						else:
							pass #TODO:virer!
					elif(i[0]=="g"):
						self.isParked=True
						self.factory.clients.goto(self.id,i[1:])
						if(self.factory.clients.park.is_inside(self.id,i[1:])):
							#TODO:rendre a inutile
							tab=self.factory.clients.park.get(i[1:])[:]
						#	tab.pop(tab.index(self.id))
							if(len(tab)>0):
								self.write("t"+str(tab)[1:-1].replace(" ","")+":"+i[1:])
					elif(i[0]=="l"):
						self.factory.clients.left(self.id,i[1:])
					#envoie des infos
					elif(i[0]=="x"):
						self.factory.save.add(self.id,i[1:])
						value="c§§"+str(self.id)+"§§"+str(self.factory.save.get(self.id))
						self.factory.clients.write_all(self.id,value)
					elif(i[0]=="c"):
						liste=i[1:].split(",")
						chaine=""
						for compteur in liste:
							value=self.factory.save.get(compteur)
							if(value!=None):
								chaine+="§§"+str(compteur)+"§§"+str(value)
						self.write("c"+str(chaine))
					
					elif(i[0]=="s"):
						dest=0
						if(i[1]==":"):
							if("s" in i[1:]):
								dest=i[1:][1:i[1:].index("s")]
								i=i[1:][i[1:].index("s"):]
								try:
									dest=int(dest)
								except(ValueError):
									dest=0	
										
						if(":" in i):
							nb=i[1:i.index(":")]
							try:
								nb=int(nb)
								pt=i.index(":")+1+nb
								if(len(i)-pt>0):#il faut un nom de salon + un msg à envoyer
									name=i[pt-nb:pt]
									msg=i[pt:]
									self.factory.clients.write(self.id,name,msg,dest)
							except(ValueError):
								pass
					elif(i[0:7]=="<policy"):
						self.factory.info.msg("Request")
						self.transport.write(crossdomain)
						self.close()
					else:
						self.factory.info.msg("Other:"+str(i)+"]")
				else:
					if(i=="q"):
						self.close()
						
	def isNotParked(self):
		if(not self.isParked):
			self.transport.loseConnection()
	def write(self,msg):
		if(len(msg)>0):
			print "ou "+str(self.id)+" : "+msg
			self.transport.write("["+msg+"]")
class SaveOnline:
	def __init__(self):
		self.save=[]
		self.listeId=[]
	def add(self,ids,value):
		ids=str(ids)
		#deja des infos
		try:
			idtab=self.listeId.index(ids)
			self.save[idtab]=value
		#aucune info
		except(ValueError):
			idtab=len(self.listeId)
			self.listeId.append(ids)
			self.save.append(value)
	def get(self,ids):
		#deja des infos
		try:
			ids=str(ids)
			idtab=self.listeId.index(ids)
			return self.save[idtab]
		#aucune info
		except(ValueError):
			return None
	def rem(self,ids):
		#deja des infos
		try:
			idtab=self.listeId.index(ids)
			self.save.pop(idtab)
			self.listeId.pop(idtab)
		#aucune info
		except(ValueError):
			pass
class QOTDFactory(Factory):
    protocol = QOTD
    def __init__(self, quote,infos,max_clients):
	self.info=infos
	self.num_clients=0
        self.password = quote
	self.save=SaveOnline()
	self.clients=Clients(max_clients)
	self.info.max_clients(self.clients.max_clients())
	#self.info=UpdateInfo()
	#self.info.start()
class UpdateInfo:
	def __init__(self):
		self.Terminated = False
		self.__msg=""
		self.max_info_shown=20
		self.start_time=time.time()
	def port(self,num_port):
		print "port"+str(num_port)
	def num_clients(self,num):
		#print "nb"+str(num)
		pass
	def max_clients(self,num):
		pass
	def msg(self,msg):
		print msg
	def stop(self):
		time.sleep(5)
        	self.Terminated = True


##Initialisation##
 
#print "Démarrage du serveur"
#print "Ecoute sur le port "+str(port)
#print "mot de passe : "+mdp
infos=UpdateInfo()
try:
	infos.msg("Lancement serveur sur le port "+str(port))
	reactor.listenTCP(port, QOTDFactory(mdp,infos,50))
	reactor.run(installSignalHandlers=0)
except(CannotListenError):
	infos.msg("Impossible d'écouter sur le port : "+str(port))
	infos.stop()
except(ValueError), error:
	infos.msg("arfffff:"+str(error))

